from pathlib import Path
from pyecharts import options as opts
from pyecharts.charts import Scatter
from pyecharts.commons.utils import JsCode


class KeywordDistributionGenerator:
    """核心词分布散点图生成器"""

    def __init__(self):
        self.colors = ["#5470c6", "#91cc75", "#fac858", "#ee6666", "#73c0de"]

    def _validate_data(self, data):
        """验证数据有效性"""
        if not data:
            raise ValueError("没有核心词数据")

        if len(data) > 5:
            raise ValueError("核心词超过5个")

        years = set()
        for keyword_data in data.values():
            if not isinstance(keyword_data, dict):
                raise TypeError("年度数据格式错误")
            for year, count in keyword_data.items():
                if not isinstance(year, int) or year < 2019 or year > 2023:
                    raise ValueError(f"年份 {year} 无效")
                if not isinstance(count, (int, float)) or count < 0:
                    raise ValueError(f"专利数量无效: {count}")
                years.add(year)

    def generate(self, data, save_path, title=None):
        """生成核心词分布散点图"""
        try:
            # 验证数据
            self._validate_data(data)

            # 处理保存路径
            save_path = Path(save_path)
            save_path.parent.mkdir(parents=True, exist_ok=True)

            # 创建散点图实例
            scatter = Scatter(
                init_opts=opts.InitOpts(
                    width="600px",
                    height="400px",
                    bg_color="#ffffff"
                )
            )

            # 添加数据系列
            for i, (keyword, yearly_data) in enumerate(data.items()):
                x_data = list(yearly_data.keys())
                y_data = [yearly_data[x] for x in x_data]  # 直接使用专利数量作为y轴数据

                # 动态计算散点大小
                max_count = max(y_data)  # 当前核心词的最大专利数量
                min_count = min(y_data)  # 当前核心词的最小专利数量
                # 按比例缩放散点大小，最小尺寸为10，最大尺寸为30
                symbol_sizes = [
                    30 + (count - min_count) / (max_count - min_count) * 30
                    for count in y_data
                ]

                scatter.add_xaxis(x_data)
                scatter.add_yaxis(
                    series_name=keyword,
                    y_axis=y_data,  # 直接使用专利数量
                    symbol_size=symbol_sizes,
                    label_opts=opts.LabelOpts(
                        is_show=False
                    ),
                    itemstyle_opts=opts.ItemStyleOpts(color=self.colors[i])
                )

            # 设置全局配置
            scatter.set_global_opts(
                title_opts=opts.TitleOpts(
                    is_show=False,
                    title=title or "核心词专利分布(2019-2023)",
                    subtitle="按年份统计各核心词专利申请数量",
                    pos_left="center",
                    pos_top="5%",
                    padding=[0, 0, 50, 0],
                    title_textstyle_opts=opts.TextStyleOpts(
                        font_size=24,
                        color="#000"
                    ),
                    subtitle_textstyle_opts=opts.TextStyleOpts(
                        font_size=14,
                        color="#666"
                    )
                ),
                legend_opts=opts.LegendOpts(
                    pos_top="5%",
                    pos_right="5%"
                ),
                tooltip_opts=opts.TooltipOpts(
                    formatter=JsCode(
                        """
                        function (params) {
                            return `
                                <b>核心词：${params.seriesName}</b><br/>
                                年份：${params.value[0]}年<br/>
                                专利数量：${params.value[1]}
                            `;
                        }
                        """
                    )
                ),
                xaxis_opts=opts.AxisOpts(
                    type_="value",
                    name="年份",
                    min_=2019,
                    max_=2023,
                    interval=1,
                    name_location="end",
                    splitline_opts=opts.SplitLineOpts(is_show=True),
                    axislabel_opts=opts.LabelOpts(formatter="{value}年")
                ),
                yaxis_opts=opts.AxisOpts(
                    type_="value",  # 改为value类型
                    name="专利数量",
                    name_location="end",
                    name_gap=35,
                    splitline_opts=opts.SplitLineOpts(is_show=True)
                ),
                toolbox_opts=opts.ToolboxOpts(
                    is_show=False,
                    pos_top="5%",
                    pos_right="5%",
                    feature={
                        "dataView": {},
                        "saveAsImage": {}
                    }
                )
            )

            # 渲染图表
            scatter.render(save_path)
            return str(save_path)

        except Exception as e:
            print(f"Error details: {str(e)}")
            raise
